using System;
using System.Diagnostics;
using System.Threading;
using System.IO;
using System.Collections;

namespace Widgetsphere.Core.Logging
{
	public sealed partial class MultiProcessFileTraceListener : TraceListener
	{
		private Thread _writeThread;
		private MultiProcessLogFileWriter _logFile;
		private const long _def_fileSize = 1024 * 1024 * 5;

		public MultiProcessFileTraceListener(string input)
		{
			string[] initializeData = input.Split('?');
			long fileSize;
			if (initializeData.Length > 1)
			{
				try
				{
					fileSize = long.Parse(initializeData[1]);
				}
				catch
				{
					fileSize = _def_fileSize;
				}
			}
			else
			{
				fileSize = _def_fileSize;
			}
			_logFile = new MultiProcessLogFileWriter(initializeData[0], fileSize);
			_writeThread = new Thread(new ThreadStart(_logFile.ThreadStartProc));
			_writeThread.Start();
		}

		override public void Write(string message)
		{
			_logFile.Write(message);
		}

		override public void WriteLine(string message)
		{
			_logFile.WriteLine(message);
		}

		private bool _disposed = false;
		protected override void Dispose(bool disposing)
		{
			base.Dispose(disposing);
			if (disposing)
			{
				if (!_disposed)
				{
					_logFile.Dispose();
					Thread.Sleep(1000);
					if (_writeThread != null && _writeThread.ThreadState != System.Threading.ThreadState.Aborted)
						_writeThread.Abort();
				}
			}
			else
			{
				if (!_disposed)
				{
					if (_writeThread != null && _writeThread.ThreadState != System.Threading.ThreadState.Aborted)
						_writeThread.Abort();
				}
			}
		}

		~MultiProcessFileTraceListener()
		{
			Dispose(false);
		}


		#region Class MultiProcessLogFileWriter
		private partial class MultiProcessLogFileWriter : IDisposable
		{
			private long _fileSize;

			#region member variables
			private Mutex _mutex;
			private FileInfo _file;
			private ArrayList _logStatementBuffer;
			private AutoResetEvent _addedToList;
			private bool _kill = false;
			#endregion

			#region constructors
			public MultiProcessLogFileWriter(string fileName, long fileSize)
			{
				_fileSize = fileSize;
				_addedToList = new AutoResetEvent(false);
				_logStatementBuffer = new ArrayList();
				_file = new FileInfo(fileName);
				InitializeMutex(fileName);
			}
			#endregion

			#region thread start
			public void ThreadStartProc()
			{
				try
				{
					while (!_kill)
					{
						_addedToList.WaitOne();
						FlushBuffer();
					}
				}
				catch (Exception ex)
				{
					MessageLog.LogLogFailure(ex.ToString());
				}
			}
			#endregion

			#region write methods
			public void Write(string message)
			{
				lock (_logStatementBuffer)
				{
					LogStatement ls = new LogStatement(false, message);
					_logStatementBuffer.Add(ls);
				}
				_addedToList.Set();
			}

			public void WriteLine(string message)
			{
				lock (_logStatementBuffer)
				{
					LogStatement ls = new LogStatement(true, message);
					_logStatementBuffer.Add(ls);
				}
				_addedToList.Set();
			}
			#endregion

			#region private helpers
			private void InitializeMutex(string fileName)
			{
				string mutexId = fileName.Replace(":", "_");
				mutexId = mutexId.Replace(@"\", "_");
				mutexId = mutexId.Replace("/", "_");
				mutexId = mutexId.Replace(".", "_");
				_mutex = new Mutex(false, mutexId);
			}

			private ArrayList GetBufferCopy()
			{
				ArrayList copyOfBuffer = null;
				lock (_logStatementBuffer)
				{
					copyOfBuffer = new ArrayList(_logStatementBuffer);
					_logStatementBuffer.Clear();
				}
				return copyOfBuffer;
			}

			private void FlushBuffer()
			{
				_mutex.WaitOne();
				try
				{
					ArrayList logBufferCopy = GetBufferCopy();
					if (logBufferCopy != null)
					{
						WriteStatements(logBufferCopy);
						_file.Refresh();
						if (_file.Exists && _file.Length > _fileSize)
						{
							string destFileName = DateTime.UtcNow.ToString("yyyyMMdd-hhmmss") + "_" + System.Guid.NewGuid() + "_" + _file.Name;
							destFileName = _file.DirectoryName + "/" + destFileName;
							_file.CopyTo(destFileName, true);
							_file.Delete();
						}
					}
				}
				catch (Exception ex)
				{
					MessageLog.LogLogFailure(ex.ToString());
				}
				finally
				{
					_mutex.ReleaseMutex();
				}
			}

			private void WriteStatements(ArrayList logStatements)
			{
				StreamWriter sw = null;
				if (!_file.Exists)
				{
					sw = _file.CreateText();
				}
				else
				{
					sw = _file.AppendText();
				}

				using (sw)
				{
					foreach (LogStatement ls in logStatements)
					{
						if (ls.WriteLine)
						{
							sw.WriteLine(ls.Message);
						}
						else
						{
							sw.Write(ls.Message);
						}
					}
				}
			}
			#endregion

			#region Struct LogStatement
			private struct LogStatement
			{
				public LogStatement(bool writeLine, string message)
				{
					WriteLine = writeLine;
					Message = message;
				}
				public bool WriteLine;
				public string Message;
			}
			#endregion

			#region IDisposable Members
			bool _disposed = false;
			protected virtual void Dispose(bool disposing)
			{
				if (!_disposed)
				{
					_kill = true;
					if (_addedToList != null)
						_addedToList.Set();
				}
			}

			public void Dispose()
			{
				Dispose(true);
			}

			~MultiProcessLogFileWriter()
			{
				Dispose(false);
			}
			#endregion
		}

		#endregion

	}

}
